// https://blog.csdn.net/qq_32036091/article/details/120608863
// 什么是new呢？
// new运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一。
// 举个例子
function Thin_usr(name, age) {
    this.name = name,
    this.age = age
}

Thin_usr.prototype.eatToMuch = function() {
    console.log('i eat so much, but i\'m very thin!!!');
}
Thin_usr.prototype.isThin = true;

// const xiaobao = new Thin_usr('zcxiaobao', 18);
// console.log(xiaobao.name);   // zcxiaobao
// console.log(xiaobao.age);    // 18
// console.log(xiaobao.isThin); // true
// // i eat so much, but i'm very thin!!!
// xiaobao.eatToMuch(); 
/**
 通过上述例子，我可以发现xiaobao可以：
   1. 访问到构造函数Thin_User中属性 
   2. 访问到Thin_User.prototype中属性

 描述得更直白一点，new做了这些事：
   1. 创建了一个空对象，对象的__proto__->Thin_User.prototype
   2. 执行构造函数，并将this指向新对象
   3. 返回新对象
 */

// 由于new是关键字，我们无法像模拟数组高阶方法一样覆盖，因此我们写一个函数createObject，来模拟new的效果。使用具体如下：

/**
 function Thin_User(name, age) {}

 const u1 = new Thin_user(...)
 const u2 = createObject(Thin_User, ...a\)

 初步模拟
 根据上面分析，createObject编写的大致步骤为：
  1. 创建一个新对象obj
  2. 设置obj.__proto__->constructor.prototype
  (但JavaScript不推荐直接修改__proto__属性，提供了setPrototypeOf方法来专门修改原型)
  3. 使用constructor.call/apply(obj, ...)，将属性添加到obj上
  4. 返回obj
 */

// 第一版本
function createObject(Con) {
    // 创建新对象obj
    // var obj = {};也可以
    var obj = Object.create(null);

    // 将obj.__proto__ -> 构造函数原型
    // (不推荐)obj.__proto__ = Con.prototype
    Object.setPrototypeOf(obj, Con.prototype);

    // 执行构造函数，并接受构造函数返回值
    // [].slice.call(arguments, 1) arguments -> Array
    const ret = Con.apply(obj, [].slice.call(arguments, 1));

    // 若构造函数返回值为对象，直接返回该对象
    // 否则返回obj
    return typeof(ret) === 'object' ? ret: obj;
}

let xiaobao2 = createObject(Thin_usr, "sanfeng_hu", 18)
console.log(xiaobao2)
xiaobao2.eatToMuch()
